package com.xxgc.springboot.config;

import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.google.gson.Gson;
import com.xxgc.springboot.config.annotation.RequiresGuest;
import com.xxgc.springboot.config.annotation.RequiresPermissions;
import com.xxgc.springboot.controller.exception.custom.NoPermissionException;
import com.xxgc.springboot.controller.exception.custom.TokenInvalidException;
import com.xxgc.springboot.controller.exception.custom.TokenNullException;
import com.xxgc.springboot.service.IUsersService;
import com.xxgc.springboot.util.JWTUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.List;

/**
 * @program: springboot
 * @description: 自定义拦截器
 * @author: Mr.abel(ShiJiaYi)
 * @create: 2022-09-26 19:31
 **/
@Slf4j
public class MyInterceptor implements HandlerInterceptor {

    private Gson gson = new Gson();

    @Autowired
    private IUsersService ius;

    /**
     * 当url已经匹配到controller层中某个方法时，在方法前执行
     * 它决定是否放行 返回ture放行 返回false不会执行
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //如果不是映射到handler方法的就直接放行
        if(!(handler instanceof HandlerMethod)){
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        //获取调用的哪个方法
        Method method = handlerMethod.getMethod();
        RequiresGuest guest = method.getAnnotation(RequiresGuest.class);
        //有这个注解就不进行权限验证
        if(guest != null){
            return true;
        }

        /*-------------JWT Token---------------*/
        String token = request.getHeader("token");
        //token是否存在
        /*---------------认证---------------*/
        if(StringUtils.isEmpty(token)){
            throw new TokenNullException("SpringBoot");
        }
        //验证token的有效性
        token = token.replace("\"","");
        String id;
        try {
            DecodedJWT verify = JWTUtils.verify(token);
            Claim claim = verify.getClaims().get("id");
            id = claim.asString();
        }catch (JWTDecodeException e){
            /**
             * 一、保存信息到数据库
             * 二、保存信息到日志
             * 三、封设备和ip
             * 四、短信或邮件通知管理员
             */
            //查询1分内，这个ip有多少个错误请求token
            //如果大于10 就封锁当前ip+浏览器的请求 5分钟（5分钟之后通过Quartz进行解封）
            //并同时通过邮件告知管理员服务器状态

            throw new TokenInvalidException("SpringBoot");
        }


        /*---------------授权------------------*/
        //获取方法上的注解
        RequiresPermissions annotation = method.getAnnotation(RequiresPermissions.class);
        //如果方法没有对应的权限注解就直接通过
        if(annotation == null){
            return true;
        }
        /*-------------------通过数据库拿到当前用户有哪些权限--------------*/
        int idInt = Integer.parseInt(id);
        //通过数据库查权限集合
        List<String> preList = ius.selectPerByUserId(idInt);
        if(preList.contains(annotation.value())){
            return true;
        }else{
            throw new NoPermissionException("SpringBoot");
        }
    }

    /**
     * 在controller执行完毕之后调用DispatcherServlet之前执行
     */
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    /**
     * 在整个请求处理完成之后（包括视图渲染）后执行，做一些垃圾清理工作
     * 这个只会在preHandle为true才会执行
     */
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}
